#pragma once

#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef socklen_t SOCKET;
#endif
#include "InternetAddress.h"
#include "CoreException.h"
#include "RawData.h"

namespace Core
{

	enum Protocols
	{
		Protocol_Tcp,
		Protocol_Udp
	};

	class Socket
	{
	protected:
		InternetAddress 	m_LocalAddress;
		InternetAddress		m_RemoteAddress;
		Protocols			m_Protocol;
		SOCKET				m_SocketDescriptor;
		bool				m_Active;

		/**
		 * Constructor Socket dengan parameter socket descriptor
		 * @param p_SocketDesc socket descriptor yang baru
		 */
		Socket(SOCKET p_SocketDesc);

		void SetSockOption();

		void Error();

	public:
		/**
		 * Constructor Socket
		 * @param p_Protocol Protocols yang digunakan
		 */
		Socket(Protocols p_Protocol);

		/**
		 * Destructor Socket
		 */
		virtual	~Socket();

		void Init(Protocols p_Protocol);

		/**
		 * Bind Socket ke PortNumber tertentu
		 * @param p_PortNumber local PortNumber number
		 * @param p_IP local IP address
		 */
		virtual void Bind(InternetAddress p_InternetAddress);

		/**
		 * Melakukan koneksi dengan remote address tertentu
		 * @param p_PortNumber PortNumber number remote host
		 * @param p_IP IP address remote host
		 */
		virtual void Connect(InternetAddress p_InternetAddress);

		/**
		 * Method untung mengeset Socket agar menunggu data yang datang
		 */
		virtual void Listen(int p_QueueNumber = 20);

		/**
		 * Accept connection dari remote address
		 * @return Socket baru yang menangani koneksi tersebut
		 */
		virtual Socket Accept(int p_TimeOutMilliSeconds = -1);

		/**
		 * Mengirim object RawData ke remote host
		 * @param p_RawData object RawData yang akan dikirim
		 */
		virtual bool Send(RawData& p_RawData, int p_TimeOutMilliSeconds = -1);

		/**
		 * Menerima RawData dari remote host
		 * @param p_RawData object RawData dari remote host
		 */
		virtual bool Receive(RawData& p_RawData, int p_TimeOutMilliSeconds = -1);

		virtual bool SendTo(RawData& p_RawData, InternetAddress& p_InternetAddress, int p_TimeOutMilliSeconds = -1);

		virtual bool ReceiveFrom(RawData& p_RawData, InternetAddress& p_InternetAddress, int p_TimeOutMilliSeconds = -1);

		InternetAddress GetLocalAddress();
		InternetAddress GetRemoteAddress();

		SOCKET GetSocketDescriptor();

		/**
		 * Menutup koneksi dan melepas resource
		 */
		void Close();

		bool IsActive();

		/**
		Karol's mod
		**/
		void JoinGroup(const InternetAddress& pGroup);

	}; //end Class Socket

}
